package com.github.panpf.sketch.test

import android.os.Looper
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.panpf.sketch.cache.CachePolicy
import com.github.panpf.sketch.request.DownloadRequest
import com.github.panpf.sketch.request.DownloadResult
import com.github.panpf.sketch.request.Listener
import com.github.panpf.sketch.test.utils.TestHttpStack
import com.github.panpf.tools4j.reflect.ktx.setFieldValue
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class SketchTest {

    @Test
    fun testEnqueueDownload() {
        val (context, sketch) = contextAndSketch()
        val oldHttpStack = sketch.httpStack
        try {
            sketch.setFieldValue("httpStack", TestHttpStack(context))

            /*
             * success
             */
            val normalDownloadListenerSupervisor = DownloadListenerSupervisor()
            val normalCallbackActionList = normalDownloadListenerSupervisor.callbackActionList
            val normalRequest = DownloadRequest(context, TestHttpStack.testUris.first().uriString) {
                listener(normalDownloadListenerSupervisor)
            }
            val normalDisposable = sketch.enqueue(normalRequest)
            runBlocking {
                normalDisposable.job.await()
            }.apply {
                Assert.assertTrue(this is DownloadResult.Success)
            }
            Assert.assertEquals("onStart, onSuccess", normalCallbackActionList.joinToString())

            /*
             * cancel
             */
            sketch.setFieldValue("httpStack", TestHttpStack(context, readDelayMillis = 1000))
            val cancelDownloadListenerSupervisor = DownloadListenerSupervisor()
            val cancelCallbackActionList = cancelDownloadListenerSupervisor.callbackActionList
            val cancelRequest = DownloadRequest(context, TestHttpStack.testUris.first().uriString) {
                downloadDiskCachePolicy(CachePolicy.DISABLED)
                listener(cancelDownloadListenerSupervisor)
            }
            val cancelDisposable =
                sketch.enqueue(cancelRequest)
            runBlocking {
                delay(1000)
                cancelDisposable.dispose()
                cancelDisposable.job.join()
            }
            Assert.assertEquals("onStart, onCancel", cancelCallbackActionList.joinToString())

            /*
             * error
             */
            val errorDownloadListenerSupervisor = DownloadListenerSupervisor()
            val errorCallbackActionList = errorDownloadListenerSupervisor.callbackActionList
            val errorTestUri = TestHttpStack.TestUri("http://fake.jpeg", 43235)
            val errorRequest = DownloadRequest(context, errorTestUri.uriString) {
                downloadDiskCachePolicy(CachePolicy.DISABLED)
                listener(errorDownloadListenerSupervisor)
            }
            val errorDisposable = sketch.enqueue(errorRequest)
            runBlocking {
                errorDisposable.job.await()
            }.apply {
                Assert.assertTrue(this is DownloadResult.Error)
            }
            Assert.assertEquals("onStart, onError", errorCallbackActionList.joinToString())
        } catch (e: Exception) {
            sketch.setFieldValue("httpStack", oldHttpStack)
        }
    }

    @Test
    fun testExecuteDownload() {
        val (context, sketch) = contextAndSketch()
        val oldHttpStack = sketch.httpStack
        try {
            sketch.setFieldValue("httpStack", TestHttpStack(context))

            /*
             * success
             */
            val normalRequest = DownloadRequest(context, TestHttpStack.testUris.first().uriString)
            runBlocking {
                sketch.execute(normalRequest)
            }.apply {
                Assert.assertTrue(this is DownloadResult.Success)
            }

            /*
             * cancel
             */
            sketch.setFieldValue("httpStack", TestHttpStack(context, readDelayMillis = 1000))
            val cancelRequest = DownloadRequest(context, TestHttpStack.testUris.first().uriString) {
                downloadDiskCachePolicy(CachePolicy.DISABLED)
            }
            runBlocking {
                val job = launch {
                    sketch.execute(cancelRequest)
                }
                delay(1000)
                job.cancelAndJoin()
            }

            /*
             * error
             */
            val errorTestUri = TestHttpStack.TestUri("http://fake.jpeg", 43235)
            val errorRequest = DownloadRequest(context, errorTestUri.uriString) {
                downloadDiskCachePolicy(CachePolicy.DISABLED)
            }
            runBlocking {
                sketch.execute(errorRequest)
            }.apply {
                Assert.assertTrue(this is DownloadResult.Error)
            }
        } catch (e: Exception) {
            sketch.setFieldValue("httpStack", oldHttpStack)
        }
    }

    @Test
    fun test() {
        // todo Write test cases
    }

    private class DownloadListenerSupervisor :
        Listener<DownloadRequest, DownloadResult.Success, DownloadResult.Error> {

        val callbackActionList = mutableListOf<String>()

        override fun onStart(request: DownloadRequest) {
            super.onStart(request)
            check(Looper.getMainLooper() === Looper.myLooper())
            callbackActionList.add("onStart")
        }

        override fun onCancel(request: DownloadRequest) {
            super.onCancel(request)
            check(Looper.getMainLooper() === Looper.myLooper())
            callbackActionList.add("onCancel")
        }

        override fun onError(request: DownloadRequest, result: DownloadResult.Error) {
            super.onError(request, result)
            check(Looper.getMainLooper() === Looper.myLooper())
            callbackActionList.add("onError")
        }

        override fun onSuccess(request: DownloadRequest, result: DownloadResult.Success) {
            super.onSuccess(request, result)
            check(Looper.getMainLooper() === Looper.myLooper())
            callbackActionList.add("onSuccess")
        }
    }
}